<?php
namespace Btr\DurakBundle\Durak;

use Symfony\Component\HttpFoundation\Session\Session; 

class Durak
{
	/**
	 * Constant value which means minimum number of cards a player should hold.
	 * @var Integer
	 */
	const LIMIT = 6;
	
	/**
	 * Constant value which is used by session to store the game.
	 * @var string
	 */
	const SAVE_NAME = 'GAME';
	/**
	 * All cards left on deck
	 * @var Cards
	 */
	private $cards;
	
	/**
	 * The human player
	 * @var Player
	 */
	private $human;
	
	/**
	 * The computer player
	 * @var ComputerPlayer
	 */
	private $computer;
	
	/**
	 * The attacker who is human or computer player.
	 * @var integer
	 */
	private $attacker;
	
	/**
	 * The defender who is human or computer player.
	 * @var integer
	 */
	private $defender;
	
	/**
	 * 
	 * @var array
	 */
	private $rounds;
	
	/**
	 * Last card attacked.
	 * @var integer;
	 */
	private $lastAttackCard;
	
	
	private static	$players = array('human','computer');
	/**
	 *  New Game.
	 */
	public function __construct()
	{
		// Initialize cards
		$this->cards = new Cards();
		$this->attacker = null;
		$this->defender = null;
		$this->rounds = array();
	}
	
	/**
	 * Start the game.
	 * @param boolean $auto  
	 * @return array
	 */
	public function start($auto)
	{
		// Take cards for human
		$cards = $this->cards->removeCards(self::LIMIT);
  		$this->human = $auto ? new ComputerPlayer($cards) : new Player($cards);
  		
 		// Take cards for computer
		$this->computer = new ComputerPlayer( $this->cards->removeCards(self::LIMIT));
	
		// Determine who is attacker.
		$index = array_rand(self::$players);
		$index = 0;
		
		$this->attacker = $index;
		$this->defender = ($index+1) % 2;
		
		// return result.
		$result = array(
				'humanByValue'=>$this->human->getCardsByValue(),
				'humanBySuit'=>$this->human->getCardsBySuit(),
				'computerByValue'=>$this->computer->getCardsByValue(),
				'computerBySuit'=>$this->computer->getCardsBySuit(),
				'masterSuit'=>$this->cards->getMasterSuit(),
				'attacker'=>$index);
		
		return $result;
		
	}
	
	/**
	 * Try to get the card which can attack.
	 * @param integer $attacker
	 * @return array   attackCard : -2 => not support, -1 => stop attack, 0 => no card attackable, >0 => card attackable.
	 */
	public function tryAttack($attacker)
	{
		$this->attacker = $attacker;
		$this->defender = ($attacker+1) % 2;	
		$attacker = $this->getAttacker();
		$card = $attacker->getAttackCard($this);
		
		$result = array('attackCard'=> $card);
		if ( $card === -2 )
		{
			$result['cardsAttackable'] = $attacker->getCardsAttackable($this);
		}
		
		if ( $card > 0 )
		{
			$this->lastAttackCard = $card;
			$defender = $this->getDefender();
			$result['rounds'] = count($this->rounds);
			$result['cardsDefendable'] = $defender->getCardsDefendable($this, $card);
		}
		return $result;
	}
	
	/**
	 * Try to find a card to defend.
	 * @param integer $defender
	 * @param integer $attackCard
	 * @return array
	 */
	public function tryDefend($defender, $attackCard)
	{
		$this->attacker = ($defender+1) % 2;
		$this->defender = $defender;
		
		$attacker = $this->getAttacker();
		$attacker->attack($attackCard);

		$defender = $this->getDefender();
		$card = $defender->getDefendCard($this, $attackCard);
		
		$result = array('defendCard'=> $card);

		if ( $card === -2 )
		{
			$result['cardsDefendable'] = $defender->getCardsDefendable($this, $attackCard);
		}
		
		if ( $card > 0 )
		{
			$this->rounds[$attackCard] = $card;
			$result['rounds'] = count($this->rounds);
		}
 		return $result;
	}
	
	/**
	 * A card was attacked by attacker.
	 * @param integer $attacker
	 * @param integer $attackCard
	 * @return array
	 */
	public function attack( $attacker, $attackCard )
	{
		$this->lastAttackCard = $attackCard;
		$this->attacker = $attacker;
		$attacker = $this->getAttacker();
		$attacker->attack($attackCard);
		return array();
	}
	
	/**
	 * A card was defended by defender
	 * @param integer $defender
	 * @param integer $defendCard
	 * @return array
	 */
	public function defend($defender, $defendCard)
	{
		$this->defender = $defender;	
		$defender = $this->getDefender();
		$defender->defend($defendCard);
		$this->rounds[$this->lastAttackCard] = $defendCard;
		return array('rounds'=> count($this->rounds));			
	}
	
	/**
	 * Stop attack.
	 * @param integer $attacker
	 * @return array
	 */
	public function endAttack($attacker)
	{
		$this->rounds = array();
		$this->attacker = $attacker;	
		$this->defender = ($attacker+1) % 2;
		
		$attacker = $this->getAttacker();		
		$defender = $this->getDefender();

		foreach ( array($attacker,$defender) as $player)
		{
			$cardsLeft = count($this->cards);
			if ( $cardsLeft == 0 ) break;
			$cards = $player->getCards();
			$num = self::LIMIT - count($cards);
			if ($num > 0)
			{
				$player->addCards($this->cards->removeCards($num));
			}
		}
		
		// return result.
		$result = array(
				'humanByValue'=>$this->human->getCardsByValue(),
				'humanBySuit'=>$this->human->getCardsBySuit(),
				'computerByValue'=>$this->computer->getCardsByValue(),
				'computerBySuit'=>$this->computer->getCardsBySuit(),
				'attacker'=>$this->defender);
		return $result;
	}

	/**
	 * Check the player can defend automatically
	 * @param integer $defender
	 * @return boolean
	 */
	public function checkAutoAccept($defender)
	{
		$this->defender = $defender;
		$defender = $this->getDefender();
		return $defender->isAutoAccept();
	}
	
	/**
	 * Can not defend, accept cards.
	 * @param integer $defender
	 * @return array
	 */
	public function accept($defender)
	{
		// Get cards attatcked/defended.
		$cardsInRounds = array();
		foreach ($this->rounds as $attackCard => $defendCard) 
		{
			$cardsInRounds[] = $attackCard;
			$cardsInRounds[] = $defendCard; 
		}
		$cardsInRounds[]=$this->lastAttackCard;

		// Clear rounds
		$this->rounds = array();
		
		// Get attacker and defender.
		$this->attacker = ($defender+1) % 2;
		$this->defender = $defender;
		$attacker = $this->getAttacker();
		$defender = $this->getDefender();
		
		// Defender accepts cards.
		$defender->addCards($cardsInRounds);
		
		// Attacker takes cards.
		$cardsLeft = count($this->cards);
		if ( $cardsLeft >0 )
		{
			$cards = $attacker->getCards();
			$num = self::LIMIT - count($cards);
			if ($num > 0)
			{
				$attacker->addCards($this->cards->removeCards($num));
			}
		}
		return array(
				'humanByValue'=>$this->human->getCardsByValue(),
				'humanBySuit'=>$this->human->getCardsBySuit(),
				'computerByValue'=>$this->computer->getCardsByValue(),
				'computerBySuit'=>$this->computer->getCardsBySuit(),
				'attacker'=>$this->attacker);
	}

	/**
	 * Check the game is done.
	 * @return array
	 */
	public function getGameDone()
	{
		$result = array('done'=>false, 'msg'=>'');
		// Check the game is over.
		if ( count($this->cards) == 0 )
		{
			$numHumanCards = count($this->human->getCards());
			$numComputerCards = count($this->computer->getCards());
				
			if ( $numHumanCards == 0 || $numComputerCards == 0 )
			{ // Done
				$result['done'] = true;
				if ( $numHumanCards == 0 && $numComputerCards == 0)
				{
					$result['msg'] = 'Draw';
				}
				elseif ( $numHumanCards == 0)
				{
					$result['msg'] = 'Computer loses.';
				}
				else
				{
					$result['msg'] = 'You lose.';
				}
			}
		}
		return $result;
	}
	
	/**
	 *  Store current game.
	 */
	public function saveGame()
	{
		$session = new Session();
		$session->set(self::SAVE_NAME, $this);	        		
	}
	
	/**
	 * Get the current game.
	 * @return Durak
	 */
	public static function retrieveGame()
	{
		$session = new Session();
		return $session->get(self::SAVE_NAME);
	}
	
	/**
	 *
	 * @return the Cards
	 */
	public function getCards() 
	{
		return $this->cards;
	}
	
	/**
	 *
	 * @return the array
	 */
	public function getRounds() 
	{
		return $this->rounds;
	}
	
	/**
	 *
	 * @return Player
	 */
	public function getAttacker() 
	{
		$varName = self::$players[$this->attacker];
		return $this->$varName;
	}
	
	/**
	 *
	 * @return Player
	 */
	public function getDefender() 
	{
		$varName = self::$players[$this->defender];
		return $this->$varName;
	}
	
}
